#ifndef STEERING_BEHAVIOR_H
#define STEERING_BEHAVIOR_H


#include "GraphEdge.h"
#include <iostream>
#include <vector>
#include "Path.h"
#include "Vector2D.h"
#include "Wall2D.h"
#include "Moving Entity.h"
#include "Character.h"
using namespace std;

class SteeringBehavior
{
private:
	enum behavior_type
  {
    none               = 0x00000,
    seek               = 0x00002,
    flee               = 0x00004,
    arrive             = 0x00008,
    wander             = 0x00010,
    obstacle_avoidance = 0x00020,
    wall_avoidance     = 0x00040,
    follow_path        = 0x00080,
    pursuit            = 0x00100,
    evade              = 0x00200,
    interpose          = 0x00400,
    offset_pursuit     = 0x00800,
	separation         = 0x01000,
	wait			   = 0x02000
  };

	MovingEntity* m_pMovingEntity;
	

	// the steering force created by the combined effect of all the selected behavoirs
	Vector2D m_vSteeringForce;

	MovingEntity* m_pTargetAgent1;
	MovingEntity* m_pTargetAgent2;
	list<Character*> m_GroupNeighbors; // The members of the group m_pMovingEntity is in. For seperation.
	Vector2D m_vTarget;

	// length of the 'detection box' utilized in obstacle avoidance
	double m_dDboxLength;

	// A buffer to contain the feelers required for wall avoidance
	vector<Vector2D> m_Feelers;

	// the length of the feelers used in wall detection.
	double m_dWallDetectionFeelerLength;

	// The current position on the wander circle the agent is attempting to steer towards
	Vector2D m_vWanderTarget;

public:
	static double m_dWanderJitter;
	static double m_dWanderRadius;
	static double m_dWanderDistance;

	static double m_dWeightWander;
	static double m_dWeightObstacleAvoidance;
	static double m_dWeightWallAvoidance;
	static double m_dWeightSeek;
	static double m_dWeightFlee;
	static double m_dWeightArrive;
	static double m_dWeightPursuit;
	static double m_dWeightOffsetPursuit;
	static double m_dWeightInterpose;
	static double m_dWeightEvade;
	static double m_dWeightFollowPath;

	// The distance (squared) a MovingEntity has to be from a path waypoint before
	// it starts seeking to the next waypoint
	static double m_dWaypointSeekDistSq;

private:

	// Pointer to the current path
	Path* m_pPath;


	// any offset used for formations or offset pursuit
	Vector2D m_vOffset;

	// binary flags to indicate whether or not a behvior is active
	int m_iFlags;

	//Arrive makes use of these to determine how quickly a MovingEntity
	//should decelerate to its target
	enum Deceleration{slow = 3, normal = 2, fast = 1};

	Deceleration m_Deceleration;

	//this function tests if a specific bit of m_iFlags is set
	bool      On(behavior_type bt){return (m_iFlags & bt) == bt;}

	bool      AccumulateForce(Vector2D &sf, Vector2D ForceToAdd);

	//creates the antenna utilized by the wall avoidance behavior
	void      CreateFeelers();

	//this behavior returns a vector that moves the agent away
	//from a target position
	Vector2D Flee(Vector2D TargetPos);

	Vector2D Seek(Vector2D TargetPos);

	//this behavior is similar to seek but it attempts to arrive 
	//at the target position with a zero velocity
	Vector2D Arrive(Vector2D     TargetPos,
				  Deceleration deceleration);

	//this behavior predicts where an agent will be in time T and seeks
	//towards that point to intercept it.
	Vector2D Pursuit(MovingEntity* agent);

	//this behavior maintains a position, in the direction of offset
	//from the target MovingEntity
	Vector2D OffsetPursuit(MovingEntity*  leader, const Vector2D offset);


	//this behavior attempts to evade a pursuer
	Vector2D Evade(const MovingEntity* agent);

	//this behavior makes the agent wander about randomly
	Vector2D Wander();

	//this returns a steering force which will attempt to keep the agent 
	//away from any obstacles it may encounter
	Vector2D ObstacleAvoidance();

	//this returns a steering force which will keep the agent away from any
	//walls it may encounter
	Vector2D WallAvoidance(const std::vector<Wall2D> &walls);


	//given a series of Vector2Ds, this method produces a force that will
	//move the agent along the waypoints in order
	Vector2D FollowPath();

	//this results in a steering force that attempts to steer the MovingEntity
	//to the center of the vector connecting two moving agents.
	Vector2D Interpose(const MovingEntity* MovingEntityA, const MovingEntity* MovingEntityB);

	Vector2D Separation(list<Character*>&  neighbors);



public: 
	SteeringBehavior(MovingEntity* agent);

	void DrawPath() { if(m_pPath) m_pPath->Draw();}
	Path* GetPath() { return m_pPath;}

	virtual ~SteeringBehavior();

	//calculates and sums the steering forces from any active behavior
	Vector2D Calculate();

	//calculates the component of the steering force that is parallel
	//with the MovingEntity heading
	double    ForwardComponent();

	//calculates the component of the steering force that is perpendicuar
	//with the MovingEntity heading
	double    SideComponent();

	void      SetTarget(const Vector2D t){m_vTarget = t;}
	Vector2D  GetTarget() {return m_vTarget;}
	void	  DrawTarget();

	void      SetTargetAgent1(MovingEntity* Agent){m_pTargetAgent1 = Agent;}
	void      SetTargetAgent2(MovingEntity* Agent){m_pTargetAgent2 = Agent;}

	void      SetOffset(const Vector2D offset){m_vOffset = offset;}
	Vector2D  GetOffset()const{return m_vOffset;}

	void SetPath(Path* path);

	Vector2D Force()const{return m_vSteeringForce;}

	void FleeOn(){m_iFlags |= flee;}
	void SeekOn(){m_iFlags |= seek;}
	void ArriveOn(){m_iFlags |= arrive;}
	void WanderOn(){m_iFlags |= wander;}
	void PursuitOn(MovingEntity* v){m_iFlags |= pursuit; m_pTargetAgent1 = v;}
	void EvadeOn(MovingEntity* v){m_iFlags |= evade; m_pTargetAgent1 = v;}
	void ObstacleAvoidanceOn(){m_iFlags |= obstacle_avoidance;}
	void WallAvoidanceOn(){m_iFlags |= wall_avoidance;}
	void FollowPathOn(){m_iFlags |= follow_path;}
	void InterposeOn(MovingEntity* v1, MovingEntity* v2){m_iFlags |= interpose; m_pTargetAgent1 = v1; m_pTargetAgent2 = v2;}
	void OffsetPursuitOn(MovingEntity* v1, const Vector2D offset){m_iFlags |= offset_pursuit; m_vOffset = offset; m_pTargetAgent1 = v1;}  
	void SeparationOn(list<Character*> aList) {m_iFlags |= separation; m_GroupNeighbors = aList;}
	void WaitOn() {m_iFlags |= wait;}

	void FleeOff()  {if(On(flee))   m_iFlags ^=flee;}
	void SeekOff()  {if(On(seek))   m_iFlags ^=seek;}
	void ArriveOff(){if(On(arrive)) m_iFlags ^=arrive;}
	void WanderOff(){if(On(wander)) m_iFlags ^=wander;}
	void PursuitOff(){if(On(pursuit)) m_iFlags ^=pursuit;}
	void EvadeOff(){if(On(evade)) m_iFlags ^=evade;}
	void ObstacleAvoidanceOff(){if(On(obstacle_avoidance)) m_iFlags ^=obstacle_avoidance;}
	void WallAvoidanceOff(){if(On(wall_avoidance)) m_iFlags ^=wall_avoidance;}
	void FollowPathOff(){if(On(follow_path)) m_iFlags ^=follow_path; SetPath(NULL);}
	void InterposeOff(){if(On(interpose)) m_iFlags ^=interpose;}
	void OffsetPursuitOff(){if(On(offset_pursuit)) m_iFlags ^=offset_pursuit;}
	void SeparationOff() {if(On(separation)) m_iFlags ^=separation;}
	void WaitOff() {if(On(wait)) m_iFlags ^= wait;}

	bool isFleeOn(){return On(flee);}
	bool isSeekOn(){return On(seek);}
	bool isArriveOn(){return On(arrive);}
	bool isWanderOn(){return On(wander);}
	bool isPursuitOn(){return On(pursuit);}
	bool isEvadeOn(){return On(evade);}
	bool isObstacleAvoidanceOn(){return On(obstacle_avoidance);}
	bool isWallAvoidanceOn(){return On(wall_avoidance);}
	bool isFollowPathOn(){return On(follow_path);}
	bool isInterposeOn(){return On(interpose);}
	bool isOffsetPursuitOn(){return On(offset_pursuit);}
	bool isSeparationOn() {return On(separation);}
	bool isWaitOn() {return On(wait);}

	const std::vector<Vector2D>& GetFeelers()const{return m_Feelers;}

	double WanderJitter()const{return m_dWanderJitter;}
	double WanderDistance()const{return m_dWanderDistance;}
	double WanderRadius()const{return m_dWanderRadius;}
};

#endif